﻿/* Copyright © 2010 Nolics
Nolics.net and NSQLObjects are trademarks of Nolics.

Microsoft Public License (Ms-PL)

This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.

2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.

3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop;


namespace NSQLObjects.DBModelClassifier
{
    internal class TSQLEditor: IDisposable
    {

        private static Dictionary<ITextBuffer, TSQLEditor> dictionary = new Dictionary<ITextBuffer, TSQLEditor>();

        public static void CreateAndShowEditor(QuerySmartTaggerProvider site, ITextBuffer model, SnapshotSpan span) {
            if (dictionary.ContainsKey(model)) {
                dictionary[model].showWindow(span);
                return;
            }

            TSQLEditor editor = new TSQLEditor(site);
            editor.createWindow(span);
            dictionary.Add(model, editor);
            model.Changed += new EventHandler<TextContentChangedEventArgs>(model_Changed);
            editor.showWindow();
        }

        static void model_Changed(object sender, TextContentChangedEventArgs e) {
            ITextBuffer model = sender as ITextBuffer;
            TSQLEditor editor = dictionary[model];
            if(editor.docFrame != null)
                editor.docFrame.Hide();
        }

        private IVsWindowFrame docFrame = null;
        private uint eventCookie = 0;
        private uint fileCookie = 0;
        private QuerySmartTaggerProvider site;
        private string file = null;

        public TSQLEditor(QuerySmartTaggerProvider site) {
            this.site = site;
        }

        private SnapshotSpan span;
        private void createWindow(SnapshotSpan span) {
            this.span = span;
            IVsUIShellOpenDocument shellOpenDoc = site.ServiceProvider.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument;

            IVsUIHierarchy pUIHier;
            uint itemID;
            Guid logicalView = VSConstants.LOGVIEWID_Code;
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider ppSP = null;
            Guid editorType = new Guid("{c5b506fb-13ba-4381-3a48-41275c4ca2b5}");   // Transact-SQL Editor

            string sql = stripDelimiters(span.Snapshot.GetText(span.Span));
            file = Path.GetTempFileName().Replace(".tmp", ".sql");
            File.WriteAllText(file, sql);

            var rc = shellOpenDoc.OpenDocumentViaProjectWithSpecific(
                file,
                (uint)(__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_UseEditor | __VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen),
                ref editorType,
                null,
                ref logicalView,
                out ppSP,
                out pUIHier,
                out itemID,
                out docFrame);

            if (docFrame != null) {
                IVsRunningDocumentTable rdtService = site.ServiceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
                object o;
                docFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocCookie, out o);
                fileCookie = (uint)(int)o;
                rdtService.AdviseRunningDocTableEvents(new watcher(file, this, fileCookie), out eventCookie);
            }

            return;
        }

        private void showWindow() {
            if (docFrame != null)
                docFrame.Show();
        }

        private void showWindow(SnapshotSpan span) {
            this.span = span;
            string sql = stripDelimiters(span.Snapshot.GetText(span.Span));

            uint flags, rlocks, elocks;
            string doc;
            IVsHierarchy pUIHier;
            uint itemID;
            IntPtr punkData = IntPtr.Zero;

            IVsRunningDocumentTable rdtService = site.ServiceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
            try {
                rdtService.GetDocumentInfo(
                    fileCookie,
                    out flags,
                    out rlocks,
                    out elocks,
                    out doc,
                    out pUIHier,
                    out itemID,
                    out punkData);

                var buffer = Marshal.GetObjectForIUnknown(punkData) as VsTextBuffer;
                int len;
                buffer.GetSize(out len);
                var buffer2 = site.EditorAdapterFactory.GetDocumentBuffer(buffer);
                buffer2.Delete(new Span(0, len));
                buffer2.Insert(0, sql);
            } finally {
                if(punkData != IntPtr.Zero)
                    Marshal.Release(punkData);
            }

            if (docFrame != null) 
                docFrame.Show();
        }

        private string stripDelimiters(string p) {
            return p.TrimStart('[').TrimEnd(']');
        }

        #region IDisposable Members

        public void Dispose() {
            List<ITextBuffer> list = new List<ITextBuffer>();
            if (dictionary.ContainsValue(this)) {
                foreach (var entry in dictionary.Where(e => e.Value == this)) {
                    entry.Key.Changed -= new EventHandler<TextContentChangedEventArgs>(model_Changed);
                    list.Add(entry.Key);
                }
            }

            foreach(var item in list)
                dictionary.Remove(item);

            if (eventCookie != 0) {
                IVsRunningDocumentTable rdtService = site.ServiceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
                rdtService.UnadviseRunningDocTableEvents(eventCookie);
                eventCookie = 0;
            }

            if (docFrame != null)
                docFrame.CloseFrame((int)__FRAMECLOSE.FRAMECLOSE_NoSave);

            docFrame = null;

        }

        #endregion

        #region Watcher

        class watcher : IVsRunningDocTableEvents
        {
            private string file = null;
            private uint cookie;
            private TSQLEditor editor;

            public watcher(string file, TSQLEditor editor, uint cookie) {
                this.editor = editor;
                this.file = file;
                this.cookie = cookie;
            }

            #region IVsRunningDocTableEvents Members

            public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) {
                return VSConstants.S_OK;
            }

            public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) {
                return VSConstants.S_OK;
            }

            public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) {
                return VSConstants.S_OK;
            }

            public int OnAfterSave(uint docCookie) {
                if (docCookie != cookie)
                    return VSConstants.S_OK;
                string sql = "[[ " + File.ReadAllText(file) + " ]]";
                editor.span.Snapshot.TextBuffer.Replace(editor.span.Span, sql);
                editor.docFrame.Hide();
                return VSConstants.S_OK;
            }

            public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) {
                return VSConstants.S_OK;
            }

            public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) {
                return VSConstants.S_OK;
            }

            #endregion
        }

        #endregion

    }


    // IVsUIHierarchy
#if NEVER
    public class myHierarchy: IVsUIHierarchy {
    #region IVsUIHierarchy Members

        public int AdviseHierarchyEvents(IVsHierarchyEvents pEventSink, out uint pdwCookie) {
            pdwCookie = 0;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Close() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int ExecCommand(uint itemid, ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetCanonicalName(uint itemid, out string pbstrName) {
            pbstrName = "";
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetGuidProperty(uint itemid, int propid, out Guid pguid) {
            pguid = Guid.Empty;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetNestedHierarchy(uint itemid, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pitemidNested) {
            ppHierarchyNested = IntPtr.Zero;
            pitemidNested = 0;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetProperty(uint itemid, int propid, out object pvar) {
            pvar = null;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetSite(out Microsoft.VisualStudio.OLE.Interop.IServiceProvider ppSP) {
            ppSP = site;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int ParseCanonicalName(string pszName, out uint pitemid) {
            pitemid = 1;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int QueryClose(out int pfCanClose) {
            pfCanClose = 1;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, Microsoft.VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int SetGuidProperty(uint itemid, int propid, ref Guid rguid) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int SetProperty(uint itemid, int propid, object var) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        private Microsoft.VisualStudio.OLE.Interop.IServiceProvider site = null;
        public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int UnadviseHierarchyEvents(uint dwCookie) {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Unused0() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Unused1() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Unused2() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Unused3() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int Unused4() {
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        #endregion
    }
#endif

}
